home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / ArticleSet.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  12.2 KB  |  541 lines

  1. #import "Alexandra.h"
  2. #import "Article.h"
  3. #import "ArticleSet.h"
  4. #import "NewsgroupSet.h"
  5. #import "Newsgroup.h"
  6. #import "ArticleViewControl.h"
  7. #import "Composer.h"
  8. #import "ColumnMatrix.h"
  9. #import "ArticleSetMatrix.h"
  10. #include <assert.h>
  11. #import <misckit/MiscMailApp.h>
  12. #import <misckit/MiscString.h>
  13. #import <misckit/MiscAppDefaults.h>
  14. #import "response_codes.h"
  15. #import "plain-subject.h"
  16. #import "MatrixScroller.h"
  17. #import "descriptors.h"
  18.  
  19. @implementation ArticleSet
  20.  
  21. - init
  22. {
  23.    currentNewsgroup=nil;
  24.    unselAction=@selector(clear);
  25.    [super init];
  26.    return self;
  27. }
  28.  
  29. - awakeFromNib
  30. {
  31.    char buf1[255];
  32.  
  33.    sprintf(buf1,"SortType %s",[nntpServer serverName]);
  34.    currentSortType=[NXApp defaultIntValue:buf1];
  35.  
  36.    return self;
  37. }
  38.  
  39. - free
  40. {
  41.    [Composer killComposerForServer:nntpServer];
  42.    return [super free];
  43. }
  44.  
  45. - loadGroup:(Newsgroup *)aNewsgroup
  46. {
  47.    int i,j;
  48.    Article *anArticle;
  49.  
  50.    if(aNewsgroup==nil)
  51.       return self;
  52.  
  53.    currentNewsgroup=aNewsgroup;
  54.  
  55.    //get article list
  56.    myMList=[aNewsgroup articleList];
  57.    [myMatrix setMatrixCellList:myMList];
  58.    
  59.    //choose articles to show. Here: all unread
  60.    j=[myMList count];
  61.    for(i=0;i<j;i++){
  62.       anArticle=[myMList objectAt:i];
  63.       if([anArticle isRead])
  64.          [anArticle unsetTag];
  65.       else
  66.          [anArticle setTag];
  67.    }
  68.    [myMatrix setAutodisplay:NO]; 
  69.    [[myMatrix loadMatrix] scrollUp];
  70.    [[myMatrix setAutodisplay:YES] display];
  71.    [self sync];
  72.  
  73.    return self;
  74. }
  75.  
  76. - selectArticle:sender
  77. {
  78.    int statusCode;
  79.    Article *oldSelection;
  80.  
  81.    [[myMatrix window] makeFirstResponder:myMatrix];
  82.  
  83.    assert(myMList!=NULL); 
  84.    oldSelection=currentSelection;
  85.    [self syncAndReturnList:NO];
  86.  
  87.    if(oldSelection==currentSelection)
  88.       return self;
  89.  
  90.    if(numSelCells==1){
  91.       statusCode=[theArticleViewControl loadArticle:currentSelection fromGroup:[[theNewsgroupSet currentSelection] stringValue]];
  92.       if(statusCode!=OK_BODY){
  93.          if(statusCode==-1)
  94.             return self;
  95.          //[myMList removeObject:currentSelection];
  96.          if([currentSelection isRead]==FALSE){
  97.             [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
  98.             [theNewsgroupSet redisplayMatrix];
  99.          }
  100.          [myMatrix perform:@selector(removeInvalidCell:) with:currentSelection afterDelay:0.0 cancelPrevious:YES];
  101.          return self;
  102.       }
  103.       if([currentSelection isRead]==FALSE){
  104.          [currentSelection setRead];
  105.          [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
  106.          [theNewsgroupSet redisplayMatrix];
  107.       }
  108.    } 
  109.  
  110.    [myMatrix display];
  111.   
  112.    return self;
  113. }
  114.  
  115. - showAll:sender
  116. {
  117.    if((currentNewsgroup==nil) || (myMList==nil))
  118.       return self;
  119.    [myMList makeObjectsPerform:@selector(setTag)];
  120.    [[myMatrix reloadMatrix] display];
  121.  
  122.    [myMatrix setButtonTitle:"All Articles"];
  123.  
  124.    return self;
  125. }
  126.  
  127. - showUnread:sender
  128. {
  129.    int i,j;
  130.    int oldNumSelCells;
  131.  
  132.    if((currentNewsgroup==NULL) || (myMList==NULL))
  133.       return self;
  134.    j=[myMList count];
  135.    for(i=0;i<j;i++){
  136.       Article* anArticle;
  137.       anArticle=[myMList objectAt:i];
  138.       if([anArticle isRead]==TRUE)
  139.          [anArticle unsetTag];
  140.       else
  141.          [anArticle setTag];
  142.    }
  143.  
  144.    if(currentSelection!=nil)
  145.       [currentSelection setTag];
  146.  
  147.    [[myMatrix reloadMatrix] display];
  148.  
  149.    oldNumSelCells=numSelCells;
  150.    [self sync];
  151.    
  152.    if((oldNumSelCells!=1)&&(numSelCells==1)){
  153.       currentSelection=nil;
  154.       numSelCells=0;
  155.       [self selectArticle:self];
  156.    }
  157.    [myMatrix setButtonTitle:"Unread Articles"];
  158.  
  159.    return self;
  160. }
  161.  
  162. - markSelectionReadOrUnread:(BOOL)flag
  163. {
  164.    /*flag==TRUE <=> Mark unread */
  165.    Article* anArticle;
  166.    int delta;
  167.    List *articleList=nil;
  168.    int i,j;
  169.  
  170.    if(currentNewsgroup==NULL)
  171.       return self;
  172.    articleList=[myMatrix getCurrSelections];
  173.    j=[articleList count];
  174.    delta=0;
  175.    for(i=0;i<j;i++){
  176.       anArticle=[articleList objectAt:i];
  177.       if([anArticle isRead]==flag){
  178.          if(flag==FALSE){
  179.             [anArticle setRead];
  180.             delta-=1;
  181.          }
  182.          else{
  183.             [anArticle setUnread];
  184.             delta+=1;
  185.          }
  186.       } 
  187.    }
  188.    if(delta!=0){
  189.       [[theNewsgroupSet currentSelection] incNumberUnreadArticles:delta];
  190.       [theNewsgroupSet redisplayMatrix];
  191.       [myMatrix display];
  192.    }
  193.    [articleList free];
  194.    return self;
  195. }
  196.  
  197. - markRead:sender
  198. {
  199.    [self markSelectionReadOrUnread:FALSE];
  200.    return self;
  201. }
  202.  
  203. - markUnread:sender
  204. {
  205.    [self markSelectionReadOrUnread:TRUE];
  206.    return self;
  207. }
  208.  
  209. - followupToSelection:sender
  210. {
  211.    if(currentSelection!=nil){
  212.       if([self postToSelectedNG:self]!=nil)
  213.          [theComposer followup:self];
  214.    }
  215.    return self;
  216. }
  217.  
  218. - postToSelectedNG:sender
  219. {
  220.    List *selList;
  221.    int i,j;
  222.  
  223.    EM_DURING
  224.      
  225.       selList=[[theNewsgroupSet theMatrix] getCurrSelections];
  226.  
  227.       // warum muss den eine Gruppe ausgewählt sein?
  228.       EM_CONDERROR(([selList count]==0),ECOMPNeedGroup,NULL,NULL);
  229.  
  230.       // See if posting is possible in general
  231.        if([nntpServer canPost]==FALSE)
  232.          EM_ERROR(ECOMPNoPosting,NULL,NULL);
  233.  
  234.       // See if you can post to all groups in list
  235.       j=[selList count];
  236.       for(i=0;i<j;i++){
  237.          char p=[[selList objectAt:i] postable];
  238.     
  239.          if(p=='n')    
  240.             EM_ERROR(ECOMPNoPostingToGroup,[[selList objectAt:i] stringValue],NULL);
  241.       }
  242.       // O.K.
  243.       theComposer=[Composer newWindowForServer:nntpServer];
  244.       [theComposer preparePostTo:selList 
  245.           selArticle:currentSelection inView:theArticleViewControl];
  246.  
  247.    EM_EMPTYHANDLER
  248.  
  249.    [selList free];
  250.    return self;
  251. }
  252.  
  253.  
  254. - clearMatrix
  255. {
  256.    currentNewsgroup=nil;
  257.    myMList=nil;
  258.    [myMatrix setMatrixCellList:nil];
  259.    [[myMatrix loadMatrix] display];
  260.    [[myMatrix window] flushWindow];
  261.    [self sync];
  262.    return self;
  263. }
  264.  
  265. - (const char *)stringValue
  266. {
  267.    return ptr_to_articlebody;
  268. }
  269.  
  270. - forwardArticle:sender
  271. {
  272.    NXStream *aStream;
  273.    int len,maxlen;
  274.    id subjectStr=[[MiscString init] alloc];
  275.     const char *oldSubjectPtr;
  276.     BOOL is_reply;
  277.     id mailer;
  278.     
  279.    if((currentSelection==nil)||(currentNewsgroup==nil)){
  280.       NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
  281.       return self;
  282.    }
  283.  
  284.    mailer=[MiscMailApp localMailer];
  285.    if(mailer==nil){
  286.       NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
  287.       return self;
  288.    }
  289.    
  290.    //O.K. first get the articles body
  291.    aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
  292.    NXPrintf(aStream,"Forwarded Article %s\n",[currentSelection header]->fieldBody[MSG_ID]);
  293.    NXPrintf(aStream,"Newsgroup %s\n",[currentNewsgroup stringValue]);
  294.    NXPrintf(aStream,"From %s\n\n",[currentSelection header]->fieldBody[FROM]);
  295.  
  296.    [theArticleViewControl writeBody:aStream];
  297.    NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);
  298.  
  299.    //Compose subject
  300.     oldSubjectPtr=plain_subject((char *)[currentSelection stringValue],&is_reply);
  301.     if(is_reply)
  302.        [subjectStr setStringValue:"Re: "];
  303.     [subjectStr strcat:oldSubjectPtr];
  304.     
  305.    // Open Compose window in mailer
  306.    [mailer sendMailTo:nil subject:currentSelection body:self];
  307.       
  308.    NXCloseMemory(aStream,NX_FREEBUFFER);
  309.    [subjectStr free];
  310.    return self;
  311. }
  312.  
  313. - replyByMail:sender
  314. {
  315.    NXStream *aStream;
  316.    int len,maxlen;
  317.    MiscString *to,*subject;
  318.    char *r;
  319.    BOOL dummy;
  320.     id mailer;
  321.     
  322.    if((currentSelection==nil)||(currentNewsgroup==nil)){
  323.       NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
  324.       return self;
  325.    }
  326.  
  327.    mailer=[MiscMailApp localMailer];
  328.    if(mailer==nil){
  329.       NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
  330.       return self;
  331.    }
  332.    
  333.    //O.K. first get the articles body
  334.    aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
  335.    NXPrintf(aStream,"In article %s, you wrote:\n",[currentSelection header]->fieldBody[MSG_ID]);
  336.    [theArticleViewControl writeQuotedText:aStream];
  337.    NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);
  338.  
  339.    //Make Subject string
  340.    subject=[[MiscString alloc] init];
  341.    [subject setStringValue:"Re: "];
  342.     [subject strcat:plain_subject([currentSelection header]->fieldBody[SUBJECT], &dummy)]; 
  343.  
  344.    //Make To
  345.    to=[[MiscString alloc] init];
  346.    r=[currentSelection header]->fieldBody[REPLY_TO];
  347.    if((r==NULL)||(r[0]=='\0'))
  348.       r=[currentSelection header]->fieldBody[FROM];
  349.    [to setStringValue:r];
  350.  
  351.    // Open Compose window in mailer
  352.    [mailer sendMailTo:to subject:subject body:self];
  353.  
  354.    NXCloseMemory(aStream,NX_FREEBUFFER);
  355.    [to free];
  356.     [subject free];
  357.     
  358.    return self;
  359. }
  360.  
  361. - cancelArticle:sender
  362. {
  363.     char    *messageID;
  364.     char    *mailAddress;
  365.     NXStream    *articlestream;
  366.  
  367.     if(currentSelection==nil)
  368.        return nil;
  369.  
  370.     if ((messageID=[currentSelection header]->fieldBody[MSG_ID]) == NULL)
  371.     return nil;
  372.     
  373.     mailAddress = [[FromHeaderController new] validFromAddress];
  374.     if(!mailAddress)
  375.         return self;
  376.  
  377.         
  378.     //Post 
  379.     articlestream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  380.     NXPrintf(articlestream,"Newsgroups: %s\nSubject: cancel\nControl: cancel %s\nFrom: %s\n\ncancel\n",[currentSelection header]->fieldBody[NEWSGROUPS], messageID, mailAddress);
  381.     NXSeek(articlestream, (long)0,NX_FROMSTART);
  382.     [nntpServer postArticle:articlestream];
  383.     
  384.     NXCloseMemory(articlestream,NX_FREEBUFFER);
  385.     free(mailAddress);
  386.     return(self);
  387. }
  388.  
  389.  
  390. - setArticleSortType:(int)type
  391. {
  392.    char buf[255];
  393.    int oldType;
  394.    id selNewsgroup;
  395.  
  396.    sprintf(buf,"SortType %s",[nntpServer serverName]);
  397.    oldType=[NXApp defaultIntValue:buf];
  398.    if(oldType!=type){
  399.       currentSortType=type;
  400.       [NXApp setDefault:buf toInt:type];
  401.       if((selNewsgroup=[theNewsgroupSet currentSelection])!=nil)
  402.          if([selNewsgroup resortIfNeeded:type]){
  403.             [myMatrix reloadMatrix];
  404.             [myMatrix display];
  405.          }
  406.    }
  407.  
  408.    return self;
  409. }
  410.  
  411. - sortArticlesByNumber:sender
  412. {
  413.    [self setArticleSortType:SORT_BY_NUMBER];
  414.    return self;
  415. }
  416.  
  417. - sortArticlesByDate:sender
  418. {
  419.    [self setArticleSortType:SORT_BY_DATE];
  420.    return self;
  421. }
  422.  
  423. - sortArticlesBySubject:sender
  424. {
  425.    [self setArticleSortType:SORT_BY_SUBJECT];
  426.    return self;
  427. }
  428.  
  429. - sortArticlesByName:sender
  430. {
  431.    [self setArticleSortType:SORT_BY_REAL_NAME];
  432.    return self;
  433. }
  434.  
  435. - catchUpThreadAt:(int)index
  436. {
  437.    int i,j;
  438.    id anArticle;
  439.    const char *thread_subject;
  440.    int m=0;
  441.    BOOL dummy;
  442.  
  443.    thread_subject=plain_subject([[myMatrix cellAt:index :0] header]->fieldBody[SUBJECT],&dummy);
  444.    for(i=0,j=[myMList count];i<j;i++){
  445.       anArticle=[myMList objectAt:i];
  446.       if(!strcmp(plain_subject([anArticle header]->fieldBody[SUBJECT],&dummy),thread_subject)){
  447.          if(![anArticle isRead]){
  448.             [anArticle setRead];
  449.             m++;
  450.          }
  451.       }
  452.    }
  453.    if(m>0)
  454.       [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1*m];
  455.  
  456.    return self;
  457. }
  458.  
  459. - skipThreadAndUpOrDown:(int)delta
  460. {
  461.    int i=[myMatrix selectedRow];
  462.    int a,b,s;
  463.  
  464.    if(i<0){
  465.       NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
  466.       return self;
  467.    }
  468.    [self catchUpThreadAt:i];
  469.    if(delta!=0){
  470.       [myMatrix getNumRows:&a numCols:&b];
  471.  
  472.       s=i+delta;
  473.       while((s>=0) && (s<a)){
  474.          id anArticle=[myMatrix cellAt:s :0];
  475.          if(![anArticle isRead]){
  476.             [myMatrix selectCellAt:s :0];
  477.             [self selectArticle:self];
  478.             [myMatrix scrollCellToVisible:s upperOffset:((delta==-1)? 1.5:0.0) lowerOffset:((delta==1)? 1.5:0.0)];
  479.             break;
  480.          }
  481.          s+=delta;
  482.       }
  483.    }
  484.    [myMatrix display];
  485.    [theNewsgroupSet redisplayMatrix];
  486.  
  487.    return self;
  488. }
  489.  
  490. - skipThreadAndUp:sender
  491. {
  492.    return [self skipThreadAndUpOrDown:-1];
  493. }
  494.  
  495. - skipThreadAndDown:sender
  496. {
  497.    return [self skipThreadAndUpOrDown:1];
  498. }
  499.  
  500. - (const char *)stringValueForCellAt:(int)index
  501. {
  502.    int h=[NXApp defaultIntValue:"findGeneralScope"];
  503.    if((h>=0)&&(h<XOVER_COUNT)){
  504.       char *s=[[myMList objectAt:index] header]->fieldBody[h];
  505.       if(s!=NULL)
  506.          return s;
  507.    }
  508.    return "";
  509. }
  510.  
  511. - (BOOL)selectArticleWithMsgid:(const char *)msgid
  512. {
  513.     int i,j;
  514.     
  515.     for(i=0,j=[myMList count];i<j;i++){
  516.         const char *aMsgid=[[myMList objectAt:i] header]->fieldBody[MSG_ID];
  517.         if(aMsgid && !strcmp(aMsgid,msgid))
  518.             return [self selectArticleWithNumber:[[myMList objectAt:i] number]];
  519.     }
  520.     
  521.     return FALSE;
  522. }
  523.  
  524. - (BOOL)selectArticleWithNumber:(long)snumber;
  525. {
  526.     int i,j;
  527.     
  528.     for(i=0,j=[myMList count];i<j;i++){
  529.         id aCell=[myMList objectAt:i];
  530.         if([aCell number]==snumber){
  531.             if(![aCell isTaged])
  532.                 [self showAll:self];
  533.             return [myMatrix selectArticleWithNumber:snumber];
  534.         }
  535.     }
  536.     
  537.     return FALSE;
  538. }
  539.  
  540. @end
  541.